#include<iostream>
#include<string>
#include<vector>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <functional>
#include"task.hpp"
//const int num = 0;

//这个代码等同于typedef std::function<void()> work_t
using work_t = std::function<void()>;

//taskmap tm;


//枚举设置错误码
enum
{
    ok = 0,
    Usageerror,
    pipeerror, //2
    forkerror      //3
};

//给父进程将管道管理起来
class channel
{
public:
    channel(int wfd,pid_t who):_wfd(wfd),_who(who)
    {
        //整数转字符串 
        _name = "管道channel-描述符" + std::to_string(wfd)+"-目标进程pid:"+std::to_string(who);
    }
    //给上层管道的名字
    std::string Name()
    {
        return _name;
    }
    //写管道里写
    void sand(int cmd)
    {
        ::write(_wfd,&cmd,sizeof(cmd));
    }

    ~channel()
    {}
private:
    int _wfd;//标识管道的写端
    std::string _name;//给管道名字方便排查错误
    pid_t _who;//给管道一个pid_t表示是给那个子进程的
};

void Usage(std::string proc)
{//参数个数不对，告诉用户怎么用
    std::cout << "Usage:" << proc << "程序名字-数字:创建子进程个数" << std::endl;
}

void worker()//子进程任务
{
    //未来读取命令，从0标准输入读
    //sleep(10);
    while(true)
    {
        int cmd = 0;
        //从标准输入读-更改已经修改过了
        int n= ::read(0,&cmd,sizeof(cmd));
        //读成功了
        if(n == sizeof(cmd) )
        {
            //传cmd过去-执行任务方法
            tm.excute(cmd);
        }
    }



}
//channels:输出行参数
//创建管道，进程池
//work_t work是回调，C++11讲过,让子进程执行不同任务
int intproce(int &num,std::vector<channel>& channels,work_t work)
{
       //循环创建子进程
       for(int i = 0 ; i < num ; i++)
       {
           //1.先有管道 
           int pipefd[2] = {0};
           //创建管道
           int n = pipe(pipefd);
           if(n < 0) return pipeerror;
   
           pid_t id = fork();//2.创建子进程
           //pid_t id = fork();
           if(id < 0) return forkerror;
           //3.建立通信信道
           if(id == 0)
           {
               ::close(pipefd[1]);//关闭写端口
   
               //子进程
               //我想让子进程都从标准输入读未来的命令
               //pipefd管道让读端，从标准输入里读
               dup2(pipefd[0],0);
               work();//子进程任务
               ::exit(1);
           }
   
           //父进程执行
           ::close(pipefd[0]);//关闭读端
           //emplace_back自动构造并插入
           channels.emplace_back(pipefd[1],id);
   
   
           // //定义一个管道类
           // channel ch(pipefd[1],id,);
           // //插入vector
           // channels.push_back(ch);
       }
       return ok;
}

void debugprintf(std::vector<channel>& channels)
{
    for(auto& i : channels)
    {
        std::cout <<  i.Name() << std::endl;
    }
}


int main(int argc ,char* argv[])
{
    if(argc != 2)//不等于2，说明用户不会用
    {
        //告诉用户怎么用
        Usage(argv[0]);
        return 1;
    }
    //2. 创建指定个数个进程

    //将用户输入的数字字符，转换成整数
    int num = std::stoi(argv[1]);

    //vector管理每一个管道类
    std::vector<channel> channels;
    //创建管道，进程池
    intproce(num,channels,worker);
    //intproce(num,channels,任务函数名);就可以实现不同方法了
    //intproce(num,channels,任务函数名);

    debugprintf(channels);
    int who = 0;
    //2.派发任务
    while(true)
    {
        //a.选择一个任务整数
        int task = tm.selectask();
        //b.选择一个子进程channel
        channel &curr =  channels[who++];
        //who%=size就会回到0
        who %= channels.size();

        std::cout << "###############" << std::endl;
        std::cout << "sand "<< task << "to" << curr.Name() << std::endl;
        std::cout << "###############" << std::endl;

        //c.派发任务
        curr.sand(task);
        sleep(3);
    }

    sleep(100);

    return 0;
}